Form Validation
Phad has a built-in form validation feature where validation rules are written in your form's html, with defaults based on html input type.
New Validation methods can be written in code/Controller/FormValidator.php with the definition function validatePropAttributename(string $attribute_value, mixed $user_input_value): bool. New attributes should use p- prefix. Definitions should be like validatePropPType for p-type attribute.
Available Validations
Every value runs through mulitple validation tests, and ALL of them must pass.
-
validatorattribute: Execute a custom validator. If a custom validator is provided, NO OTHER VALIDATION IS PERFORMED. (See Below) - input tagName - Automatic validation based upon the input's tag name
- input: no validation
- select: no validation (does validate options, but that's a different validations step)
- textarea:
is_string($value) - Any other tagname: always fails to validate.
- input type - Automatic validation based upon the input's type (other validation is still applied!)
- hidden, backend: no validation
- radio: no validation
- text:
is_string($value) - number:
is_numeric($value) - date:
date_create($value)MUST NOT returnfalse - time:
h:m, matching integer ranges[0-23]:[0-59] - url:
filter_var($value,FILTER_VALIDATE_URL); - phone:
is_numeric() && (is 10 characters || is 11 characters, starting with a 1) - email:
filter_var($value, FILTER_VALIDATE_EMAIL); - checkbox:
($value=='on'||$value=='1')
-
requiredattribute: Value must be present and non-empty. If value not required, and value is empty string or null, then no further validation is done & the value is considered valid.- You may omit
requiredor setrequired="false"for values that are not required.
- You may omit
-
maxlengthattribute:$value <= max length -
minlengthattribute:$value >= min length -
p-typeattribute - validate the php data type. Attribute's value can be one of: (@TODO test p-type validation)- array
- number
- int
- string
-
p-nohtmlattribute - if present and not "false", value cannot contain html. Tests input string againststrip_tags($input_string), and if they're not identical, then validation fails. -
<select>options:$valuemust be present on one of the option tags. if$value=='something'AND there is an<option value="something">, then it passes. -
radio: the submitted value MUST be one of the options defined on the same-"name"d radio inputs. (@todo write tests for radio validation) -
checkbox: @todo add checkbox validation, similar to select & radio
Sample Form
Submitting this form will fail if any of the html-defined requirements are not met. So if title is 76+ characters or is not submitted, it'll fail.
Note: The requirements are always loaded server-side by parsing the server-side copy of the form. The requirements are never submitted by the user.
<form item="Blog" target="/blog/{slug}/">
<onsubmit><?php $BlogRow['slug'] = generate_slug($BlogRow['title']); ?></onsubmit>
<input type="text" name="title" maxlength="75" required>
<textarea name="body" maxlength="2000" minlength="10" required></textarea>
<!-- backend properties are also validated -->
<input type="backend" name="slug" minlength=4 maxlength=150 />
</form>
Note: generate_slug() is NOT a built-in function.
Manual Validation From Form
Data can also be manually validated by scanning a form & calling the validator api. (Validation in pure php is available too. See below)
Note: You can alternatively use your Phad instance to laod/compile the item.
Let's say the above form is saved at $dir/form/Blog.php
<?php
$fake_phad = new class { public function __call($mthd,$args){}} // required because of bad code design.
$dir = __DIR__; // depends on your setup
$item = new \Phad\Item('form/Blog', $dir, ['phad'=>$fake_phad]);
$item_info = $item->info();
$validation_expectations = $item_info->properties;
$submitter = new \Phad\FormValidator($expectations);
// $is_valid is true here
$is_valid = $submitter->validate(
[ 'title' => 'Are cats happy?',
'body' => 'Cats are probably happy when they\'re well taken care of',
'slug' => 'are-cats-happy',
]
Manual validation Without a Form
You can define your validation requirements in php & directly use Phad's Validator without any complicated dependencies or html parsing.
<?php
// These are the same expectations defined in the form above.
// Most forms will also have an id as a hidden input, but not this example.
$expectations = [
'title' =>
[
'type' => 'text',
'maxlength' => '75',
'tagName' => 'input',
],
'body' =>
[
'maxlength' => '2000',
'minlength' => '50',
'tagName' => 'textarea',
],
'slug' =>
[
'type'=>'backend',
'minlength' => '4',
'maxlength' => '150',
'tagName'=>'input',
]
];
$submitter = new \Phad\FormValidator($expectations);
// $is_valid is true here
$is_valid = $submitter->validate(
[ 'title' => 'Are cats happy?',
'body' => 'Cats are probably happy when they\'re well taken care of',
'slug' => 'are-cats-happy',
]
);
Failed Validation
Continuing the example above, let's see how a failed validation works. Firstly, validate() will return false. Secondly, you'll get an array describing which validations failed, and another array of error messages.
<?php
// now let's fail a validation!
$is_valid = $submitter->validate(
[ 'title' => 'Are cats happy?',
'body' => 'Cats are probab', // body is < 50 chars (the minlength), so this will fail
'slug' => 'are-cats-happy',
],
$error_messages_array,
$failed_columns_array,
);
// $error_messages_array & $failed_columns_array are both passed by-reference, and will be filled as below
$failed_columns_array === [
[
'body'=> [
'failed_value' => 'Cats are probab', // the value that failed
'minlength' => 50, // the validation that caused the failure
]
]
];
$error_messages_array === [
'msg' => "'body' failed validation for 'minlength:50'",
];
Notes
If a validation fails, but no error messages are added, a generic message is added saying "[number_of] fields failed validation. Cause unkown." (Yes, the typo is in the code lol. Yes I should fix it lol.)
If extra keys/values are passed, and $submitter->allow_extra_fields == false (the default), and all other validations were successful, then a generic error message is added: "[number_of] unexpected fields were submitted. They were: [list of fields]", and validation fails.
If full form submission fails due to a validation error, but no error messages were generated, then this generic message is used: "Validation failed. Reason unkown. 'validation' error"
Custom Validator
Custom validators can be written.
This specifies that the 'tag' data should be an array of ints, using the user-defined validator u-contains-ints. Note that undefined validators always return true.
<input prop="tag" type="hidden" p-type="array" u-contains-ints />
Example php: To make this work, we have to add a custom validator.
<?php
$validator = new \Phad\FormValidator($expectations);
$validator->addAttributeValidator('u-contains-ints',
/** Ensure the value is an array containing only int values. */
function(string $attribute_name, string $attribute_value, mixed $user_input_value){
// $attribute_name is passed just in case you want to use the same callable for multiple validators.
if (!is_array($user_input_value))return false;
foreach ($user_input_value as $string_value){
$intval = (int)$string_value;
$intstring = (string)$intval; // because user-submitted data is always a string
if ($intstring!==$string_value)return false;
}
return true;
}
);